package com.qgy.springsecurity.configs;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qgy.springsecurity.dao.UserDao;
import com.qgy.springsecurity.filters.JwtAuthenticationFilter;
import com.qgy.springsecurity.models.MyResult;
import com.qgy.springsecurity.models.bases.SysUser;
import com.qgy.springsecurity.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

@Configuration
public class SecurityConfig {

    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
                .build();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/qgy/users/login").permitAll() // 登录接口允许匿名访问
                        .requestMatchers("/qgy/**").authenticated() // 其他/qgy下的路径需要认证
                        .anyRequest().denyAll() // 拒绝所有其他请求
                )
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling(exceptionHandling ->
                        exceptionHandling.authenticationEntryPoint(new JwtAuthenticationEntryPoint()) // 处理未认证的请求
                )
                .csrf(AbstractHttpConfigurer::disable) // 禁用CSRF保护
                .sessionManagement(sessionManagement ->
                        sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 设置为无状态会话管理,每一次都必须携带token
                )
                .authenticationProvider(myAuthenticationProvider);

        return http.build();
    }
}

@Component
class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(MyResult.error().message("登录失败").toString());
    }
}

@Component
class MyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDao userDao;

    @Lazy
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String principal = (String) authentication.getPrincipal();
        String credentials = (String) authentication.getCredentials();
        if (Objects.isNull(credentials)) {
            //只有一个传参，应该为token
            Long uid = JwtUtil.getSubject(principal, Long.class);
            SysUser user = userDao.selectOne(new QueryWrapper<>() {{
                eq("id", uid);
            }});
            if (Objects.isNull(user)) throw new RuntimeException("用户不存在");
            return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        } else {
            //判断账号密码是否正确
            SysUser user = userDao.selectOne(new QueryWrapper<>() {{
                eq("user_name", principal);
            }});
            if (Objects.isNull(user) || !passwordEncoder.matches(credentials, user.getPassword())) throw new RuntimeException("用户名或密码错误");
            return new UsernamePasswordAuthenticationToken(user,null,new ArrayList<>());
//            Long num = userDao.selectCount(new QueryWrapper<>() {{
//                eq("user_name", principal);
//            }});
//            if (!num.equals(1L)) throw new RuntimeException("用户名或密码错误");
//            return new UsernamePasswordAuthenticationToken(principal, null, new ArrayList<>());
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
}
